import {
  ConstraintType,
  EventsRes,
  ICircleParams,
  SceneGraph,
} from '@amcax/base';
import { ctx2 } from '@app-cad/module';
import { wsApi } from '@app-cad/server';
import { Vector2tuple, Vector3 } from 'three-legacy';
import {
  SKETCH_CENTEREVENTS,
  sketchEventCenter,
} from '../../../events/SketchEventCenter';
import CmdBase, { PickType } from '../../CmdBase';
import { dimension } from '../dimension/Dimension';
import { MouseState, State } from './types';
import {
  calculatePoint,
  isEquals,
  pointProjectOnLine,
} from '@app-cad/common/utils';

type SnapType = {
  curveId: string;
  pickType: PickType;
  point?: Vector3;
};
export default class CmdInteractor extends CmdBase {
  endPoint: Vector3; // 绘制终点 ：圆上的
  center: Vector3; // 圆心
  radius: number = 0; // 圆半径
  currentState: MouseState = State.NONE;
  private centerSnapInfo: SnapType;
  private endSnapInfo: SnapType;
  private isTangency: boolean = false; // 是否为相切圆

  constructor() {
    super();
    this.registerEvents();
  }

  reset() {
    this.currentState = State.NONE;
    this.endPoint = null;
    this.center = null;
    this.radius = 0;
    this.centerSnapInfo = null;
    this.endSnapInfo = null;
    dimension().destroy();
    this.removeTempPoint();
  }

  registerEvents() {
    dimension().reset();
    sketchEventCenter()
      .setState('SketchCircle')
      .on(SKETCH_CENTEREVENTS.POINTERMOVE, this.onMouseMove)
      .on(SKETCH_CENTEREVENTS.POINTERDOWN, this.onMouseDown)
      .on(SKETCH_CENTEREVENTS.KEYUP, this.onKeyUp)
      .addExit(this.destroy.bind(this));
  }

  unregisterEvents() {
    sketchEventCenter()
      .off(SKETCH_CENTEREVENTS.POINTERMOVE, this.onMouseMove)
      .off(SKETCH_CENTEREVENTS.POINTERDOWN, this.onMouseDown)
      .off(SKETCH_CENTEREVENTS.KEYUP, this.onKeyUp)
      .setDefault();
  }

  releaseObjects() {
    this.clearLine();
    this.assisGroup.clear();
    SceneGraph.emptyGroup(this.assisGroup);
    ctx2.viewer.requestRender();
  }

  onMouseMove = (e: EventsRes) => {
    this.setMouseWV(e, true);
    if (this.currentState === State.DRAW) {
      this.endPoint = this.mouseWV.clone();
      this.setRadius();
      this.preview();
    }
  };
  enterKeyDown = () => {
    if (this.currentState === State.DRAW) {
      dimension().determineDim();
      this.setRadius();
      this.preview();
      setTimeout(() => {
        this.finishDraw();
      }, 100);
    }
  };

  setRadius() {
    if (dimension().dimType === 'create') {
      this.radius = dimension().getDimInfo().value;
    } else {
      this.tangency();
      this.radius = this.endPoint.distanceTo(this.center);
    }
  }
  // 相切
  tangency() {
    this.isTangency = false;
    if (!this.pickSnapInfo) {
      return;
    }
    const { type, vertices, params } = this.pickSnapInfo;
    if (type === 'Line') {
      const [p1, p2]: Vector2tuple[] = vertices;
      // 获取向量
      const v1 = new Vector3(p2[0] - p1[0], p2[1] - p1[1], 0);
      const endPoint = pointProjectOnLine(this.center, new Vector3(...p1), v1);
      if (isEquals(this.center, endPoint)) {
        return;
      }
      this.endPoint = endPoint;
      this.isTangency = true;
      this.removeAssistPoint();
    } else if (type === 'Circle' || type === 'ArcOfCircle') {
      const pickCenter = new Vector3(...(params as ICircleParams).center);
      const vec = this.center.clone().sub(pickCenter).normalize();
      const vec2 = new Vector3()
        .subVectors(this.endPoint, pickCenter)
        .normalize();
      if (vec2.angleTo(vec) < Math.PI / 10) {
        this.endPoint = calculatePoint(
          vec,
          (params as ICircleParams).radius,
          pickCenter,
        );
        this.isTangency = true;
        this.removeAssistPoint();
      }
    }
  }

  onMouseDown = (e: EventsRes) => {
    if (e.button.onLeft) {
      // 鼠标左键，进行绘制
      if (this.currentState === State.NONE) {
        this.center = this.mouseWV.clone();
        this.previewLines.line = null;
        if (this.pickType !== PickType.Null) {
          this.centerSnapInfo = {
            pickType: this.pickType,
            curveId: this.pickSnapInfo.id,
            point: this.mouseWV.clone(),
          };
          this.unPickedIds = [this.pickSnapInfo.id];
        }
        // （编辑线） 添加辅助点
        this.createAssistPoint(this.center);
        this.currentState = State.DRAW;
      } else if (this.currentState === State.DRAW) {
        if (this.pickType !== PickType.Null) {
          this.endSnapInfo = {
            pickType: this.pickType,
            curveId: this.pickSnapInfo.id,
            point: this.mouseWV.clone(),
          };
        }
        this.currentState = State.FINISH;
        this.finishDraw();
      }
    } else if (e.button.onRight && !this.closeRightClick) {
      this.currentState = State.NONE;
      // 鼠标右键，取消绘制
      this.destroy();
    }
  };

  preview() {
    if (this.currentState === State.NONE) {
      return;
    }
    if (this.radius > 0) {
      dimension().dimDiameter(this.center, this.radius);
    }
    // // 绘制圆
    const params = this.getArcVertexs({
      x: this.center.x,
      y: this.center.y,
      radius: this.radius,
      startAngle: 0,
      endAngle: Math.PI * 2,
      clockWise: true,
    });
    this.createUpdateLine(params);
    ctx2.viewer.requestRender();
  }

  // 添加约束
  addConstraint() {
    const constraints = [];
    if (this.centerSnapInfo?.curveId) {
      const constraint = this.getConstraint(this.centerSnapInfo, this.center);
      constraints?.push(constraint);
    }
    if (this.isTangency) {
      const { curveId } = this.endSnapInfo;
      const type = ConstraintType.Tangent;
      constraints.push({ curve: curveId, target: '', type });
    }
    return constraints.filter((item) => item != null);
  }

  isLoading = false;

  async finishDraw() {
    if (this.isLoading) {
      return;
    }

    const annotations = [dimension().addDimension()];
    if (this.radius != 0) {
      try {
        this.isLoading = true;
        const res = await wsApi.createSketchCircle(this.sketchId, {
          center: this.getParams(),
          radius: this.radius,
          curveStyle: this.lineType,
          constraints: this.addConstraint(),
          annotations,
        });
        await this.render(
          { added: res.added },
          annotations.some((a) => a != null),
        );
      } finally {
        this.isLoading = false;
      }
      this.reset();
      this.releaseObjects();
    }
  }
  getParams(): string | [number, number] {
    if (this.centerSnapInfo?.curveId) {
      const { curveId, pickType } = this.centerSnapInfo;
      if (pickType === PickType.Point) {
        return curveId;
      }
    }
    return [this.center.x, this.center.y];
  }
}
